﻿namespace EasyToolkit.Excel.ContextualMenus
{
    using System;
    using System.Collections.Generic;
    using EasyToolkit.Tools.Extensions;
    using Microsoft.Office.Core;
    using Microsoft.Office.Interop.Excel;

    public class ContextualMenuManager : IContextualMenuManager
    {
        #region singleton
        private static readonly Lazy<IContextualMenuManager> instance = new Lazy<IContextualMenuManager>(() => Activator.CreateInstance(typeof(ContextualMenuManager), true) as IContextualMenuManager);

        /// <summary>Return the 'ContextualMenuManager' singleton instance</summary>
        public static IContextualMenuManager Instance { get { return instance.Value; } }
        #endregion

        #region attributes and properties
        private readonly object syncObj = new object();

        private List<ContextualMenuManagerHandler> menuItemManagers = new List<ContextualMenuManagerHandler>();
        private List<Workbook> manageWorkbooks = new List<Workbook>();

        public event ContextualMenuManagerHandler ContextualMenusManager
        {
            add
            {
                if (value != null)
                {
                    lock (syncObj)
                    {
                        if (!menuItemManagers.Contains(value))
                            menuItemManagers.Add(value);
                    }
                }
            }
            remove
            {
                if (value != null)
                {
                    lock (syncObj)
                    {
                        if (menuItemManagers.Contains(value))
                            menuItemManagers.Remove(value);
                    }
                }
            }
        }

        #endregion

        #region .ctors
        private ContextualMenuManager()
        {}
        #endregion

        #region public methods
        public void RegisterWorkbook(Workbook workbook)
        {
            if(workbook == null)
                return;

            lock (syncObj)
            {
                if(! manageWorkbooks.Contains(workbook))
                    workbook.SheetBeforeRightClick += OnSheetBeforeRightClickViewsManagement;
            }
        }

        public void UnRegisterWorkbook(Workbook workbook)
        {
            if (workbook == null)
                return;

            lock (syncObj)
            {
                if (manageWorkbooks.Contains(workbook))
                    workbook.SheetBeforeRightClick -= OnSheetBeforeRightClickViewsManagement;
            }
        }
        #endregion

        #region private methods
        private void OnSheetBeforeRightClickViewsManagement(object sheet, Range range, ref bool cancel)
        {
            Application application = range.Application;
            CommandBar commandBar = application.CommandBars["Cell"];
            commandBar.Reset();

            foreach (ContextualMenuManagerHandler manager in menuItemManagers)
            {
                try
                {
                    IEnumerable<IContextualMenu> menus = manager(sheet as Worksheet, range);
                    if (menus != null)
                    {
                        foreach (IContextualMenu menu in menus)
                            CreateMenu(commandBar.Controls, menu);
                    }
                }
                catch (Exception ex)
                { 
                    string methodName = manager.Method == null ? string.Empty : manager.Method.Name.EmptyIfNull();
                    throw new EasyToolkitException(string.Format("Contextual menu manager: '{0}' invocation failed: {1}.", methodName, ex.Message));
                }
            }
        }

        private void CreateMenu(CommandBarControls parentControls, IContextualMenu contextualMenu)
        {
            if (contextualMenu != null && contextualMenu.Items != null)
            {
                foreach (IContextualPart part in contextualMenu.Items)
                {
                    if (part is IContextualMenu)
                    {
                        IContextualMenu contextualSubMenu = part as IContextualMenu;
                        CommandBarPopup subMenu = (CommandBarPopup) parentControls.Add(MsoControlType.msoControlPopup, Type.Missing, Type.Missing, Type.Missing, true);
                        subMenu.Caption = contextualSubMenu.Caption;
                        subMenu.BeginGroup = contextualSubMenu.BeginGroup;
                        CreateMenu(subMenu.Controls, contextualSubMenu);
                    }
                    else
                    {
                        ContextualMenuItem contextualMenuItem = part as ContextualMenuItem;
                        if (contextualMenuItem != null)
                        {
                            MsoControlType menuItem = MsoControlType.msoControlButton;
                            CommandBarButton commandBarButton = (CommandBarButton) parentControls.Add(menuItem, Type.Missing, Type.Missing, Type.Missing, true);
                            commandBarButton.Style = MsoButtonStyle.msoButtonIconAndCaption;
                            commandBarButton.Caption = contextualMenuItem.Caption;
                            commandBarButton.BeginGroup = contextualMenuItem.BeginGroup;
                            if (contextualMenuItem.FaceId != 0)
                                commandBarButton.FaceId = contextualMenuItem.FaceId;

                            commandBarButton.Click += (CommandBarButton ctrl, ref bool cancel1) =>
                                                      {
                                                          try
                                                          {
                                                              if(contextualMenuItem.Action != null)
                                                                  contextualMenuItem.Action(); 
                                                          }
                                                          catch (Exception ex)
                                                          {
                                                            string methodName = contextualMenuItem.MethodInfo == null ? string.Empty : contextualMenuItem.MethodInfo.Name; 
                                                            throw new EasyToolkitException(string.Format("Contextual menu: '{0}' invocation failed: {1}.", methodName, ex.Message));
                                                          }};
                        }
                    }
                }
            }
        }
        #endregion
    }
}
